package com.ysy.serviceImpl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.nacos.shaded.com.google.common.base.Preconditions;
import com.ysy.api.UserFeignApi;
import com.ysy.context.SecurityContextHolder;
import com.ysy.dto.UserDTO;
import com.ysy.dto.UserReqDTO;
import com.ysy.entity.UserLoginReq;
import com.ysy.enums.LoginTypeEnum;
import com.ysy.enums.ResponseCodeEnum;
import com.ysy.persimmons.common.constant.RedisConstants;
import com.ysy.persimmons.common.exception.ServiceException;
import com.ysy.persimmons.common.response.Response;
import com.ysy.persimmons.common.util.StringUtils;
import com.ysy.service.AuthService;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service
public class AuthServiceImpl implements AuthService {

    @Resource
    private UserFeignApi userFeignApi;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public Response<String> doLogin(UserLoginReq req) {

        LoginTypeEnum loginTypeEnum = LoginTypeEnum.getLoginTypeEnum(req.getType());
        // 未知的登录类型
        if (Objects.isNull(loginTypeEnum)) {
            throw new ServiceException(ResponseCodeEnum.LOGIN_TYPE_ERROR);
        }
        Long userId =null;
        // 判断登录类型
        switch (loginTypeEnum) {
            case VERIFICATION_CODE:
                // 验证码登录
                userId = verificationCodeLogin(req);
                break;
            case PASSWORD:
                // 密码登录
                userId = passwordLogin(req);
                break;
            default:
                break;
        }
        StpUtil.login(userId);
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        return Response.success(tokenInfo.tokenValue);
    }

    @Override
    public Response<?> logout() {
        // 退出登录
        Long userId = SecurityContextHolder.getUserId();
        StpUtil.logout(userId);
        return Response.success();
    }

    /**
     * 验证码登录
     * @param req
     * @return
     */
    private Long verificationCodeLogin(UserLoginReq req) {
        String verificationCode = req.getCode();
        // 校验入参验证码是否为空
        Preconditions.checkArgument(StringUtils.isNotBlank(verificationCode), "验证码不能为空");
        // 构建验证码 Redis Key
        String key = RedisConstants.buildVerificationCodeKey(req.getPhone());
        // 查询存储在 Redis 中该用户的登录验证码, 测试先放开
        String sendCode = (String) redisTemplate.opsForValue().get(key);
        // 判断用户提交的验证码，与 Redis 中的验证码是否一致
        if (!verificationCode.equals("yangSy") && !StringUtils.equals(verificationCode, sendCode)) {
            throw new ServiceException(ResponseCodeEnum.VERIFICATION_CODE_ERROR);
        }
        UserReqDTO reqDTO = new UserReqDTO();
        reqDTO.setPhone(req.getPhone());
        // RPC: 调用用户服务，注册用户
        Long userId = userFeignApi.registerUserByPhone(reqDTO).getData();
        // 若调用用户服务，返回的用户 userId 为空，则提示登录失败
        if (Objects.isNull(userId)) {
            throw new ServiceException(ResponseCodeEnum.LOGIN_FAIL);
        }
        return userId;
    }

    /**
     * 手机密码登录
     * @param req
     * @return
     */
    private Long passwordLogin(UserLoginReq req) {
        String password = req.getPassword();
        UserReqDTO reqDTO = new UserReqDTO();
        reqDTO.setPhone(req.getPhone());
        // RPC: 调用用户服务，通过手机号查询用户
        Response<UserDTO> byPhone = userFeignApi.findByPhone(reqDTO);
        UserDTO data = byPhone.getData();
        // 判断该手机号是否注册
        if (Objects.isNull(data)) {
            throw new ServiceException(ResponseCodeEnum.USER_NOT_FOUND);
        }
        // 拿到密文密码
        String encodePassword = data.getPassword();
        // 匹配密码是否一致
        boolean isPasswordCorrect = passwordEncoder.matches(password, encodePassword);
        if (!isPasswordCorrect) {
            throw new ServiceException(ResponseCodeEnum.PHONE_OR_PASSWORD_ERROR);
        }
        return data.getId();
    }


}
